<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>简易音效生成器</title>
  <style>
    body {
      font-family: 'Microsoft YaHei', Arial, sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
      background-color: #f5f5f5;
    }
    h1 {
      color: #3a6ea5;
      text-align: center;
    }
    .container {
      background-color: white;
      padding: 20px;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    .sound-btn {
      display: block;
      width: 100%;
      padding: 15px;
      margin: 10px 0;
      background-color: #3a6ea5;
      color: white;
      border: none;
      border-radius: 5px;
      font-size: 16px;
      cursor: pointer;
      transition: background-color 0.3s;
    }
    .sound-btn:hover {
      background-color: #2a5282;
    }
    .instructions {
      background-color: #f0f7ff;
      padding: 15px;
      border-left: 5px solid #3a6ea5;
      margin: 20px 0;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>金山学院牛人榜音效生成器</h1>
    
    <div class="instructions">
      <h3>使用说明</h3>
      <p>点击下面的按钮生成简单的音效文件。下载后，请将文件移动到 public/sounds 目录下。</p>
    </div>
    
    <button class="sound-btn" onclick="generateAndDownload('drumroll')">生成鼓点声 (drumroll.mp3)</button>
    <button class="sound-btn" onclick="generateAndDownload('reveal')">生成揭晓音效 (reveal.mp3)</button>
    <button class="sound-btn" onclick="generateAndDownload('applause')">生成掌声 (applause.mp3)</button>
    <button class="sound-btn" onclick="generateAndDownload('whoosh')">生成过渡音效 (whoosh.mp3)</button>
    <button class="sound-btn" onclick="generateAndDownload('epic')">生成史诗背景音乐 (epic.mp3)</button>
    <button class="sound-btn" onclick="generateAndDownload('click')">生成点击音效 (click.mp3)</button>
    <button class="sound-btn" style="background-color: #c22;" onclick="generateAll()">一键生成所有音效</button>
  </div>

  <script>
    // 创建音频上下文
    const audioContext = new (window.AudioContext || window.webkitAudioContext)();
    
    // 生成并下载音效
    function generateAndDownload(type) {
      let buffer;
      
      switch(type) {
        case 'drumroll':
          buffer = generateDrumroll();
          break;
        case 'reveal':
          buffer = generateReveal();
          break;
        case 'applause':
          buffer = generateApplause();
          break;
        case 'whoosh':
          buffer = generateWhoosh();
          break;
        case 'epic':
          buffer = generateEpic();
          break;
        case 'click':
          buffer = generateClick();
          break;
      }
      
      // 将AudioBuffer转换为WAV
      const wav = audioBufferToWav(buffer);
      const blob = new Blob([wav], { type: 'audio/wav' });
      
      // 创建下载链接
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `${type}.mp3`;
      a.click();
      
      // 清理URL
      URL.revokeObjectURL(url);
    }
    
    // 一键生成所有音效
    function generateAll() {
      setTimeout(() => generateAndDownload('drumroll'), 0);
      setTimeout(() => generateAndDownload('reveal'), 1000);
      setTimeout(() => generateAndDownload('applause'), 2000);
      setTimeout(() => generateAndDownload('whoosh'), 3000);
      setTimeout(() => generateAndDownload('epic'), 4000);
      setTimeout(() => generateAndDownload('click'), 5000);
    }
    
    // 生成鼓点声
    function generateDrumroll() {
      const duration = 3.0;
      const buffer = audioContext.createBuffer(1, audioContext.sampleRate * duration, audioContext.sampleRate);
      const channel = buffer.getChannelData(0);
      
      for (let i = 0; i < buffer.length; i++) {
        const t = i / buffer.length;
        
        // 创建鼓点的效果
        const drumFreq = 20;
        const drumSpeed = Math.min(1, t * 3);
        const drum = Math.sin(2 * Math.PI * drumFreq * t * (1 + drumSpeed * 5));
        
        // 添加一些噪声
        const noise = (Math.random() * 2 - 1) * 0.2;
        
        // 混合并应用包络
        channel[i] = (drum * 0.8 + noise * 0.2) * Math.min(t * 3, 1) * (1 - Math.pow(t, 8) * 0.5);
      }
      
      return buffer;
    }
    
    // 生成揭晓音效
    function generateReveal() {
      const duration = 1.5;
      const buffer = audioContext.createBuffer(1, audioContext.sampleRate * duration, audioContext.sampleRate);
      const channel = buffer.getChannelData(0);
      
      for (let i = 0; i < buffer.length; i++) {
        const t = i / buffer.length;
        
        // 上升的音调
        const frequency = 220 + 880 * t;
        channel[i] = 0.5 * Math.sin(2 * Math.PI * frequency * t);
        
        // 应用淡入淡出效果
        const fadeTime = 0.1;
        if (t < fadeTime) {
          channel[i] *= t / fadeTime;
        } else if (t > 1 - fadeTime) {
          channel[i] *= (1 - t) / fadeTime;
        }
      }
      
      return buffer;
    }
    
    // 生成掌声
    function generateApplause() {
      const duration = 3.0;
      const buffer = audioContext.createBuffer(1, audioContext.sampleRate * duration, audioContext.sampleRate);
      const channel = buffer.getChannelData(0);
      
      for (let i = 0; i < buffer.length; i++) {
        const t = i / buffer.length;
        
        // 随机噪声模拟掌声
        channel[i] = (Math.random() * 2 - 1) * 0.5;
        
        // 掌声强度随时间变化
        const envelope = Math.min(t * 3, 1) * Math.pow(1 - t, 0.5);
        channel[i] *= envelope;
      }
      
      return buffer;
    }
    
    // 生成过渡音效
    function generateWhoosh() {
      const duration = 0.8;
      const buffer = audioContext.createBuffer(1, audioContext.sampleRate * duration, audioContext.sampleRate);
      const channel = buffer.getChannelData(0);
      
      for (let i = 0; i < buffer.length; i++) {
        const t = i / buffer.length;
        
        // 白噪声 + 高频滤波
        channel[i] = (Math.random() * 2 - 1) * Math.pow(1 - t, 2) * 0.8;
      }
      
      return buffer;
    }
    
    // 生成史诗背景音乐
    function generateEpic() {
      const duration = 5.0;
      const buffer = audioContext.createBuffer(1, audioContext.sampleRate * duration, audioContext.sampleRate);
      const channel = buffer.getChannelData(0);
      
      for (let i = 0; i < buffer.length; i++) {
        const t = i / buffer.length;
        
        // 主旋律
        const baseFreq = 110; // A2音符
        const note1 = Math.sin(2 * Math.PI * baseFreq * t);
        const note2 = Math.sin(2 * Math.PI * (baseFreq * 5/4) * t) * 0.5;
        const note3 = Math.sin(2 * Math.PI * (baseFreq * 3/2) * t) * 0.3;
        
        // 混合音符
        channel[i] = (note1 + note2 + note3) / 3;
        
        // 添加脉冲效果
        const pulseRate = 2; // 每秒脉冲次数
        const pulse = Math.pow(Math.sin(2 * Math.PI * pulseRate * t) * 0.5 + 0.5, 2);
        channel[i] *= 0.6 + pulse * 0.4;
        
        // 应用淡入淡出
        if (t < 0.1) {
          channel[i] *= t / 0.1;
        } else if (t > 0.9) {
          channel[i] *= (1 - t) / 0.1;
        }
      }
      
      return buffer;
    }
    
    // 生成点击音效
    function generateClick() {
      const duration = 0.15;
      const buffer = audioContext.createBuffer(1, audioContext.sampleRate * duration, audioContext.sampleRate);
      const channel = buffer.getChannelData(0);
      
      for (let i = 0; i < buffer.length; i++) {
        const t = i / buffer.length;
        
        // 简单的衰减正弦波
        channel[i] = Math.sin(2 * Math.PI * 1000 * t) * Math.exp(-15 * t);
      }
      
      return buffer;
    }
    
    // 将AudioBuffer转换为WAV
    function audioBufferToWav(buffer) {
      const numChannels = buffer.numberOfChannels;
      const sampleRate = buffer.sampleRate;
      const format = 1; // PCM
      const bitDepth = 16;
      
      const bytesPerSample = bitDepth / 8;
      const blockAlign = numChannels * bytesPerSample;
      
      const dataLength = buffer.length * numChannels * bytesPerSample;
      const bufferLength = 44 + dataLength;
      
      const arrayBuffer = new ArrayBuffer(bufferLength);
      const view = new DataView(arrayBuffer);
      
      // RIFF identifier
      writeString(view, 0, 'RIFF');
      // file length - 8
      view.setUint32(4, 36 + dataLength, true);
      // RIFF type
      writeString(view, 8, 'WAVE');
      // format chunk identifier
      writeString(view, 12, 'fmt ');
      // format chunk length
      view.setUint32(16, 16, true);
      // sample format (raw)
      view.setUint16(20, format, true);
      // channel count
      view.setUint16(22, numChannels, true);
      // sample rate
      view.setUint32(24, sampleRate, true);
      // byte rate
      view.setUint32(28, sampleRate * blockAlign, true);
      // block align
      view.setUint16(32, blockAlign, true);
      // bits per sample
      view.setUint16(34, bitDepth, true);
      // data chunk identifier
      writeString(view, 36, 'data');
      // data chunk length
      view.setUint32(40, dataLength, true);
      
      // 写入音频数据
      const offset = 44;
      const channelData = [];
      for (let i = 0; i < numChannels; i++) {
        channelData.push(buffer.getChannelData(i));
      }
      
      let pos = offset;
      for (let i = 0; i < buffer.length; i++) {
        for (let c = 0; c < numChannels; c++) {
          const sample = Math.max(-1, Math.min(1, channelData[c][i]));
          const value = (sample < 0 ? sample * 0x8000 : sample * 0x7FFF);
          view.setInt16(pos, value, true);
          pos += 2;
        }
      }
      
      return new Uint8Array(arrayBuffer);
    }
    
    function writeString(view, offset, string) {
      for (let i = 0; i < string.length; i++) {
        view.setUint8(offset + i, string.charCodeAt(i));
      }
    }
  </script>
</body>
</html> 